package main
import (
	"fmt"
	"errors"
	"time"
	"math/rand"
	"sync"
)

//创建一个结构体来关联一个环形队列数组
type ArrayQueue struct{
	front int
	rear int 
	array [10]string
	maxSize int
}

func main(){
	//创建一个等待锁,并加上2
	var wg sync.WaitGroup
	wg.Add(2)
	//创建一个队列实例
	aq := &ArrayQueue{
		front : 0,
		rear : 0,
		maxSize : 10,
	}
	//创建一个管道用于保存协程结束状态
	flagover := make(chan bool,2)
	//执行模拟来客户协程
	go AddElemToQueue(aq,flagover)
	//执行两个服务端开始工作协程
	for i:=1;i<=2;i++{
		go Server(i,&wg,aq,flagover)
	}
	//Wait等待执行完今日协程任务
	wg.Wait()
	//结束
}

//协程1 专门添加客户到队列中，随机时间间隔添加
func AddElemToQueue(aq *ArrayQueue,flagover chan bool){
	rand.Seed(time.Now().UnixNano())
	i := 1
	for {
		if i > 15{
			break
		}
		if aq.IsFull() {
			continue
		}
		n := rand.Intn(5)+1
		time.Sleep(time.Duration(n) * time.Second)
		fmt.Printf("客户%d 来了\n",i)
		aq.PushQueue(fmt.Sprintf("客户%d",i))
		i++
	}
	flagover <- true
	flagover <- true
	close(flagover)
}

//协程2 专门用于服务端服务队列中的客户
func Server(id int,wg *sync.WaitGroup,aq *ArrayQueue,flagover chan bool){
	defer wg.Done()
	rand.Seed(time.Now().UnixNano())
	for {
		select {
			case  <-flagover :
				return
			default :
		}
		
		n := rand.Intn(5)+1
		time.Sleep(time.Duration(n) * time.Second)
		if aq.IsEmpty(){
			continue
		}
		user,_ := aq.PopQueue()
		fmt.Printf("%d 号服务端 ======>  服务了 %s\n",id,user)
	
	}
	
}

//入队操作
func (this *ArrayQueue)PushQueue(user string)(err error){
	if (this.rear+1)%this.maxSize == this.front{
		fmt.Println("\n队列已满...")
		return errors.New("full...")
	}
	this.array[this.rear] = user
	this.rear = (this.rear+1)%this.maxSize
	return nil
}

//出队操作
func (this *ArrayQueue)PopQueue()(user string,err error){
	if this.front == this.rear {
		fmt.Println("\n队列已空...")
		return "nil",errors.New("empty...")
	}
	user = this.array[this.front]
	this.front = (this.front+1)%this.maxSize
	return
}

//判断队列是否已满
func (this *ArrayQueue)IsFull()bool{
	if (this.rear+1)%this.maxSize == this.front{
		return true
	}
	return false
}
//判断队列是否已空
func (this *ArrayQueue)IsEmpty()bool{
	if this.front == this.rear{
		return true
	}
	return false
}